import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
import { AutoSizer, List, ListRowProps } from 'react-virtualized';
import { getTreeCheckedLeafs, isTreeDataHasMultiLevel } from '../../utils/utility';
import { ITree } from '../../utils/utility';
import { TreeNode } from './TreeNode';

export interface IVirtualizedTreeProps {
  style?: React.CSSProperties;
  onChange?: (treeData: ITree[]) => void;
  treeData: ITree[];
}

export interface IVirtualizedTreeState {
  treeData: ITree[];
}

export class VirtualizedTree extends React.Component<IVirtualizedTreeProps, IVirtualizedTreeState> {
  public static propTypes = {
    style: PropTypes.object,
  };
  public static TreeNode = TreeNode;

  /**
   * 从treeData中筛选选中的叶子节点
   * @param treeData
   */
  public static getCheckedLeafs = (treeData: ITree[]) => {
    return treeData.reduce((result: ITree[], tree) => result.concat(getTreeCheckedLeafs(tree)), []);
  }

  public list = React.createRef<List>();

  constructor(props: any) {
    super(props);
    this.getExpandedItemCount = this.getExpandedItemCount.bind(this);
    this.rowHeight = this.rowHeight.bind(this);
    this.treeRenderer = this.treeRenderer.bind(this);
    this.handleChange = this.handleChange.bind(this);
    this.recomputeRowHeights = this.recomputeRowHeights.bind(this);
  }

  public componentDidUpdate() {
    // 每次更新完都重新计算高度，否则节点会重合
    this.recomputeRowHeights();
  }

  /** 计算节点数 */
  public getExpandedItemCount(item: any) {
    let count = 1;
    if (item.expanded && item.children) {
      count += item.children
        .map(this.getExpandedItemCount)
        .reduce((total: number, c: number) => {
          return total + c;
        }, 0);
    }
    return count;
  }

  /** 计算高度 */
  public recomputeRowHeights() {
    if (this.list.current) {
      this.list.current.recomputeRowHeights();
    }
  }

  /**
   * 判断节点是否展开，高度动态变化
   * @param param0
   */
  public rowHeight({ index }: any) {
    return this.getExpandedItemCount(this.props.treeData[index]) * TreeNode.TREE_NODE_HEIGHT;
  }

  public handleChange(tree: ITree, index: number) {
    if (this.props.onChange) {
      this.props.onChange([
        ...this.props.treeData.slice(0, index),
        tree,
        ...this.props.treeData.slice(index + 1),
      ]);
    }
  }

  public treeRenderer({ index, style, key }: ListRowProps) {
    const treeNode = this.props.treeData[index];
    const classes = {
      'br-tree': true,
      'br-tree--list': !isTreeDataHasMultiLevel(this.props.treeData),
    };
    // TreeNode使用 li标签，用ul包装一下
    return (
      <ul className={classNames(classes)} key={key} style={style}>
        <TreeNode
          treeNode={treeNode}
          onChange={(tree) => this.handleChange(tree, index)}
        />
      </ul>
    );
  }

  public render() {
    const { treeData } = this.props;
    return (
      <AutoSizer style={this.props.style}>
        {({ height, width }) => (
          <List
            height={height}
            width={width}
            ref={this.list}
            rowHeight={this.rowHeight}
            rowCount={treeData.length}
            rowRenderer={this.treeRenderer}
            overscanRowCount={20}
            style={{ outline: 0 }}
          />
        )}
      </AutoSizer>
    );
  }
}
